
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <getopt.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"
#include <math.h>
#include "testHelper.c"
#include "interface.h"

#define NUM_KINGDOM_CARDS 10
#define MIN_PLAYERS 2
#define MIN_KINGDOM_CARD adventurer
#define KINGDOM_CARD_RANGE (treasure_map - adventurer)

int getChoices(struct gameState *G, int card, int *k, int *choices);
int getPrintMode(int argc, char **argv);
int getSeed(int argc, char **argv, int printMode);
int playThisCard(struct gameState *G, int *k, int printMode);
int buyThisCard(struct gameState *G, int printMode);
int init(struct gameState *G, int *k, int seed, int printMode);
void getAndPrintWinners(int *players, int numPlayers, struct gameState *G, int printMode);
char *getCardName(int card);
void endGame(struct gameState *G, int seed);

int failState; //determines whether a test has failed.  set to 0 to start

/**********************************/
/********** MAIN FUNCTION *********/
/**********************************/
int main (int argc, char **argv) {
  int printMode, seed, runs;
  int n, numPlayers;
  int k[NUM_KINGDOM_CARDS];
  struct gameState G;
  int passCounter = 0; //global pass tracker
  int failCoutner = 0; //global fail tracker

  /* Define whether or not we should print arguments. Default: No */
  printMode = getPrintMode(argc, argv);
  seed = getSeed(argc, argv, printMode);  

  /* This section (down to initialize game) will create one
     random game if the user inputs a seed, but will create
     1000 runs with 1000 seeds if the user does not supply
     the seed. */
  if(seed == 0){ // if no user input
    runs = 1000;  
  }
  else // if user input
    runs = 1;

  for (n = 0; n < runs; n++){

    failState = 0;
    if(runs == 1000) // if no user input, then set seed = run number
      seed = n + 1;

    PutSeed((long) seed); // set the seed and then print
    if(printMode == 1){
      printf("seed = %d\n", seed);
    }

    /* Initialize Game */
    numPlayers = init(&G, k, seed, printMode);
    int players[numPlayers]; /* will be used later to get the winner */

    /* Play the game until completion */
    while(!isGameOver(&G)){
      
      /* Play Cards */
      while(G.numActions > 0){
	if((playThisCard(&G, k, printMode)) == -1){
	  endGame(&G, seed);
	}
      }

      /* Buy Cards */
      while(G.numBuys > 0){
	buyThisCard(&G, printMode);  
      }

      /* End Current Player's Turn */
      if(!isGameOver(&G)){
	endTurn(&G);
      }
      else{
  if (failState == 1){
    failCoutner++;
  }
  else{
    passCounter++;
  }
	if(printMode == 1){
	  printf("Game is Over!\n");
	}
      }
    }

    /* If game is over */
    getAndPrintWinners(players, numPlayers, &G, printMode);
  }
  /* If all tests are over */
  if (runs > 1){
  printf("\n\n******************************\n\n"
    "OVERAL RESULTS OF SYSTEM TESTS:\n%d full games completed with no errors"
    "\n%d games completed with 1 or more errors", passCounter, failCoutner);
    printf("\n\n******************************\n\n");
  }

  return 0;
}


/**************************************
 * Function getPrintMode()            *
 * Input: command line arguments      *
 * Output: whether or not printMode   *
 *         is turned on               *
 *************************************/
int getPrintMode(int argc, char **argv){
  int printMode;

  if(argc > 0 && argc < 4){
    if(argc > 1 && atoi(argv[1]) == 1){
      printMode = 1;
    }
    else if(argc > 1 && atoi(argv[1]) == 0){
      printMode = 0;
    }
    else if(argc == 1){
      printMode = 0;
    }
    else{
      printf("You have entered an incorrect print mode.\n");
      printf("Print mode can = 1 (print) or 0 (don't print).\n");
      exit(-1);
    }
  } else {
    printf("You have entered an invalid number of arguments.\n");
    printf("Usage: testdominion <printMode> [seedValue]\n");
    exit(-1);
  }

  if(printMode == 1){
    printf ("***TESTING DOMINION***\n");
    printf("printMode = %d\n", printMode);
  }

  return printMode;
}


/**************************************
 * Function getSeed()                 *
 * Input: command line arguments and  *
 *        printMode                   *
 * Output: Seed value                 *
 *************************************/
int getSeed(int argc, char **argv, int printMode){
  int seed;
  
  /* if the user inputs a value for the seed, use it */
  if(argc > 2){
    if(atoi(argv[2]) > -1 && atoi(argv[2]) < 1000){
      seed = atoi(argv[2]);
    }
    else {
      printf("You have entered a bad seed value. Please enter a value between 0 and 1000.\n");
      printf("Usage: testdominion <printMode> [seedValue]\n");
      exit(-1);
    }
  }
  /* otherwise, create a random seed for use */
  else {
    seed = 0;
  }
  
  return seed;
}


/**************************************
 * Function playThisCard()            *
 * Input: gameState and Kingdom Cards *
 * Output: boolean for success of     *
 *         playing the designated card*
 *         with the choices it        *
 *         receives from getChoices   *
 *************************************/
int playThisCard(struct gameState *G, int *k, int printMode){
  int *choices = malloc(sizeof(int) * 3);
  int curPlyr = whoseTurn(G);
  int cardToPlay, getThem, i, playACard;
  char *cardName = malloc(sizeof(char) * 15); //big enough for all the card names
  
  /* decide on a random card to play */
  i = 0;
  playACard = 1;
  cardToPlay = floor(Random() * G->handCount[curPlyr]);
  
  //if card selected is mine card, make sure it can be played (i.e. treasure cards exist in hand)
  int mineEligible;
  if (handCard(cardToPlay, G) == mine){ 
    mineEligible = 1;
    int treasure = 0;
    int j;   
    for(j = 0; j < G->handCount[curPlyr]; j++){
      if(handCard(j, G) == copper || handCard(j, G) == silver || 
        handCard(j, G) == gold){
          treasure = 1;
          //printf("outer eligible");
          break;
      }
    }
    if (treasure == 0){
      mineEligible = 0;
    }
  }
  

  while(handCard(cardToPlay, G) < adventurer || handCard(cardToPlay, G) == gardens || mineEligible == 0){
    cardToPlay = (floor(Random() * G->handCount[curPlyr]));      
    i++;
    if(i == 100){
      playACard = 0;
      break;
    }

    //if card selected is mine card, make sure it can be played (i.e. treasure cards exist in hand)
    
    if (handCard(cardToPlay, G) == mine){
      mineEligible = 1; 
      int treasure = 0;
      int j;   
      for(j = 0; j < G->handCount[curPlyr]; j++){
        if(handCard(j, G) == copper || handCard(j, G) == silver || 
          handCard(j, G) == gold){
            treasure = 1;
            //printf("inner eligible");
            //printf("treasure at position %d", j);
            //printf("and treasure is %d", handCard(j, G));
            break;
        }
      }
      if (treasure == 0){
        mineEligible = 0;
      }    

    }
  }

  if(playACard != 0){
    cardName = getCardName(handCard(cardToPlay, G));
    if(printMode == 1){
      printf("Player %d will be playing %s from position %d.\n", curPlyr, cardName, cardToPlay);
    }
    /* if the card has options, create them randomly */
    if((getThem = getChoices(G, cardToPlay, k, choices)) == -1){
      printf("Problem making choices.\n");
      failState = 1;
      return -1;
    }
    if(printMode == 1){
      printf("Choices: ");
      for(i = 0; i < 3; i++){
	printf("%d ", choices[i]);
      }
      printf("\n");
    }

    /* play the card */
    if((playCard(cardToPlay, choices[0], choices[1], choices[2], G)) == -1){
      if(printMode == 1){
	printf("Problem playing selected card.\n");  
      }
      failState = 1;
      return -1;
    }
    
    free(choices);
    return 0;
  }
  else {
    if(printMode == 1){
      printf("No cards to play\n");
    }
    G->numActions = 0;
    return 0;
  }
}


/**************************************
 * Function buyThisCard()             *
 * Input: gameState                   *
 * Output: boolean for success of     *
 *         buying the designated card *
 * Process: Tries to buy province 1st *
 *          then tries for gold and   *
 *          finally buys random card  *
 *************************************/
int buyThisCard(struct gameState *G, int printMode){
  int buy, cardToBuy;
  char *cardName = malloc(sizeof(char) * 15); //big enough for all the card names

  /* Buy Province if you have enough cash and it is available */
  if(G->coins > 7 && G->supplyCount[province] > 0){
    buyCard(province, G);
    if(printMode == 1){
      printf("Player %d bought a Province\n", whoseTurn(G));
    }
  }
  /* Buy Gold if you have enough cash and it is available */
  else if(G->coins > 5 && G->supplyCount[gold] > 0){
    buyCard(gold, G);
    if(printMode == 1){
      printf("Player %d bought a Gold\n", whoseTurn(G));
    }
  }
  /* Buy a random card if you can't afford the above two */
  else {
    if(printMode == 1){
      printf("Entering Random purchase.\n");
    }
    buy = -1;
    while(buy == -1){
      cardToBuy = floor(Random() * treasure_map);
      buy = buyCard(cardToBuy, G);
    } // found a card we could afford
    cardName = getCardName(cardToBuy);
    if(printMode == 1){
      printf("Player %d bought a %s\n", whoseTurn(G), cardName);
    }
  } // end the random buy
  return 0;
}


/**************************************
 * Function init()                    *
 * Input: gameState, kingdom cards    *
 * Output: returns number of players  *
 *         and alters the gameState   *
 *         and kingdom card arrays    *
 *************************************/
int init(struct gameState *G, int *k, int seed, int printMode){
  int i, numPlayers; //, isUnique, j, potential;
  char *cardName = malloc(sizeof(char) * 15); //long enough for any card name
  
  /* Fill the Game State with garbage */
  for (i = 0; i < sizeof(struct gameState); i++) {
    ((char*)G)[i] = floor(Random() * 256);
  }
  
  /* Set number of random players */
  numPlayers = floor(Random() * PLAYER_RANGE) + MIN_PLAYERS;
  if(printMode == 1){
    printf("Number of Players: %d\n", numPlayers);
  }

  //kingdom card array initialization.  only uses kingdom cards whose cardEffect() functions have been debugged.
  //to add another, just delete it form the list of || statements below)
  int numSelected = 0;

  while(numSelected < NUM_K_CARDS) {
    int used = 0;
    int card = floor(Random() * NUM_TOTAL_K_CARDS);
    if(card < adventurer || card == baron || card == minion
      || card == tribute || card == ambassador || card == cutpurse ||
      card == embargo || card == salvager || card == sea_hag || 
      card == treasure_map) continue;
    for(i = 0; i < numSelected; i++) {
      if(k[i] == card) {
  used = 1;
  break;
      }
    }
    if(used == 1) continue;
    k[numSelected] = card;
    numSelected++;
  }


  if(printMode == 1){
    printf("Kingdom Cards are: ");
    for(i = 0; i < NUM_KINGDOM_CARDS; i++){
      cardName = getCardName(k[i]);
      printf("%s, ", cardName);
    }
    printf("\n");
  }

  //initialize new game
  if(printMode == 1){
    printf("Initializing Game With:\n");
    printf("players = %d\n", numPlayers);
    for(i = 0; i < NUM_KINGDOM_CARDS; i++){
      printf("KingdomCard %d = %d\n", i, k[i]);
    }
    printf("seed = %d\n", seed);
  }

  if(initializeGame(numPlayers, k, seed, G) == -1){
    if(printMode == 1){
      printf("Initialize Game Failed.\n");
      failState = 1;      
    }
  }

  //free(cardName); //why could this break my code?
  return numPlayers;
}
  

/**************************************
 * Function getChoices()              *
 * Input: gameState, kingdom cards,   *
 *        hand position of card to be *
 *        played, pointer to choices  *
 * Output: returns boolean success    *
 *         and alters the choices     *
 *         array                      *
 * Process: Selects random choices    *
 *          from within the set of    *
 *          possibilities depending on*
 *          which card is being played*
 *************************************/
int getChoices(struct gameState *G, int card, int *k, int *choices){
  int curPlyr = whoseTurn(G);
  int kingdomCard, i;
  int cardPos = card;
  card = handCard(card, G);

  /* initiate all choices to -1 */
  for(i = 0; i < 3; i++){
    choices[i] = -1;
  }

  if(card == feast){
    //card must cost less than 5
    do{
      kingdomCard = floor(Random() * 10);
      choices[0] = k[kingdomCard]; // one of the kingdom cards
    } while (getCost(choices[0]) > 5 || G->supplyCount[choices[0]] <= 0);
  }	   

  if(card == mine){
    for(i = 0; i < G->handCount[curPlyr]; i++){
      if(handCard(i, G) == copper || handCard(i, G) == silver ||
        handCard(i, G) == gold){
	choices[0] = i;
      }
    }
    if(choices[0] == -1){
      printf("No treasure cards in hand to mine!\n");
    }
    else {
      choices[1] = handCard(choices[0], G) + 1; //either silver or gold
    }
  }
  
  if(card == remodel){
    //make sure card to be gained costs, at most, 2 more than card being trashed
    //make sure card selected for trashing is not remodel card being played.
    do {
    choices[0] = floor(Random() * G->handCount[curPlyr]); // a random card from player's hand    
    
    kingdomCard = floor(Random() * 10);
    choices[1] = k[kingdomCard]; // one of the kingdom cards
    
    } while (getCost(handCard(choices[0], G)) + 2 < getCost(choices[1]) && choices[0] != cardPos);
    
  }

  if(card == baron){
    choices[0] = floor(Random() * 2); // appx 50/50 of being 0 or 1
  }

  if(card == minion){
    choices[0] = floor(Random() * 2); // appx 50/50 of being 0 or 1
    if(choices[0]){
      choices[1] = 0;
    }
    else{
      choices[1] = 1;
    }
  }

  if(card == steward){
    choices[0] = floor(Random() * 3) + 1; // appx 33/33/33 of being 1 or 2 or 3
    if(choices[0] == 3){
      /* two cards to be discarded */
      choices[1] = G->hand[curPlyr][card];
      while(choices[1] == G->hand[curPlyr][card]){
	choices[1] = Random() * G->handCount[curPlyr];
      }
      choices[2] = choices[1];
      /* don't want the second card to be the same as the first or selected card */
      while(choices[2] == choices[1] || choices[2] == G->hand[curPlyr][card]){
	choices[2] = Random() * G->handCount[curPlyr];
      }
    }	
  }

  if(card == ambassador){
    int showCard = card;
    int i, j;
    j = 0;

    while(showCard == card){
      showCard = floor(Random() * G->handCount[curPlyr]); //a card in the player's hand
    }
    choices[0] = showCard;
    for(i = 0; i < G->handCount[curPlyr]; i++){
      if(i != card && i == G->hand[curPlyr][choices[0]] && i != choices[0]){
	j++; //number of cards to return to supply
      }
    }
    choices[1] = j;
  }
  
  if(card == embargo){
    kingdomCard = floor(Random() * 10);
    choices[1] = k[kingdomCard]; // one of the kingdom cards
  }

  if(card == salvager){
    choices[1] = floor(Random() * G->handCount[curPlyr]); // a card in player's hand
  }
  
  return 0;
}


/**************************************
 * Function getAndPrintWinners()      *
 * Input: array of player scores,     *
 *        number of players, gamestate*
 *        printmode                   *
 * Output: prints winners to the      *
 *         console                    *
 *************************************/
void getAndPrintWinners(int *players, int numPlayers, struct gameState *G, int printMode){
  int i;

  getWinners(players, G);
  if(printMode == 1){
    for(i = 0; i < numPlayers; i++){
      if(players[i] == 1){
	printf("Player %d is the winner!\n", i);
      }
    }
    for(i = 0; i < numPlayers; i++){
      printf("Player: %d\tScore: %d\n", i, scoreFor(i, G));
    }
  }
}


/**************************************
 * Function getCardName()             *
 * Input: card, and String for name   *
 * Output: puts the card name in the  *
 *         given array                *
 *************************************/
char *getCardName(int card){
  if(card == curse)
    return "curse";
  else if(card == estate)
    return "estate";
  else if(card == duchy)
    return "duchy";
  else if(card == province)
    return "province";
  else if(card == copper)
    return "copper";
  else if(card == silver)
    return "silver";
  else if(card == gold)
    return "gold";
  else if(card == adventurer)
    return "adventurer";
  else if(card == council_room)
    return "council_room";
  else if(card == feast)
    return "feast";
  else if(card == gardens)
    return "gardens";
  else if(card == mine)
    return "mine";
  else if(card == remodel)
    return "remodel";
  else if(card == smithy)
    return "smithy";
  else if(card == village)
    return "village";
  else if(card == baron)
    return "baron";
  else if(card == great_hall)
    return "great_hall";
  else if(card == minion)
    return "minion";
  else if(card == steward)
    return "steward";
  else if(card == tribute)
    return "tribute";
  else if(card == ambassador)
    return "ambassador";
  else if(card == cutpurse)
    return "cutpurse";
  else if(card == embargo)
    return "embargo";
  else if(card == outpost)
    return "outpost";
  else if(card == salvager)
    return "salvager";
  else if(card == sea_hag)
    return "sea_hag";
  else if(card == treasure_map)
    return "treasure_map";
  else
    return NULL;
}

void endGame(struct gameState *G, int seed){
  G->numActions = 0;
  G->numBuys = 0;
  G->supplyCount[province] = 0;
  printf("Game with seed %d failed.\n", seed);
}
